package com.zyw.zhuangdian.controller;

import com.alibaba.fastjson.JSONObject;
import com.zyw.zhuangdian.constant.ServiceConstant;
import com.zyw.zhuangdian.enums.MsgCode;
import com.zyw.zhuangdian.pojo.Resp;
import com.zyw.zhuangdian.pojo.dto.UserDto;
import com.zyw.zhuangdian.pojo.in.UserDtoIn;
import com.zyw.zhuangdian.pojo.out.UserDtoOut;
import com.zyw.zhuangdian.property.MyProperties;
import com.zyw.zhuangdian.service.CacheService;
import com.zyw.zhuangdian.service.UserService;
import com.zyw.zhuangdian.util.*;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.mindrot.jbcrypt.BCrypt;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

import javax.servlet.http.HttpServletRequest;
import java.util.Date;
import java.util.List;

@RestController
@RequestMapping(value = "/user")
@RequiredArgsConstructor
@Slf4j
public class UserController {
    private final UserService userService;
    private final CacheService cacheService;
    private final MyProperties properties;

    /**
     * 用户注册
     * @param json
     * @return
     */
    @RequestMapping(value = "/register", method = RequestMethod.POST)
    public ResponseEntity<Resp<?>> register(@RequestBody JSONObject json) {
        Assert.hasKeyAndValue(json, "username", "用户名不能为空");
        Assert.hasKeyAndValue(json, "password", "密码不能为空");

        UserDtoIn in = BeanConvertUtil.covertBean(json, UserDtoIn.class);

        // 如果有验证码或者手机号，进入手机号验证码模式
        if (StringUtils.isNotBlank(json.getString("verifyCode")) || StringUtils.isNotBlank(json.getString("phone"))) {
            String phone = json.getString("phone");
            String verifyCode = json.getString("verifyCode");
            String key = properties.getCacheVerifyCodePrefix() + phone;
            String truth = cacheService.getKey(key);
            if (StringUtils.isEmpty(truth) || !truth.equals(verifyCode)) {
                return RespUtil.OK(MsgCode.MSG_INCORRECT_VERIFY_CODE);
            }
            cacheService.delKey(key);
        }

        if(StringUtils.isBlank(in.getNickname())) {
            in.setNickname("用户_" + in.getUsername());
        }
        in.setDescription("Hello ~");
        userService.addUser(in);

        in.harmless();
        in.setPrivilege(null);
        Resp<List<UserDtoOut>> userBack = userService.listUsers(in);

        Resp<UserDtoOut> out = new Resp<>();
        UserDtoOut outUser = userBack.getData().get(0);
        outUser.harmless();
        out.setData(outUser);

        return RespUtil.OK(out);
    }

    /**
     * 用户登录
     * @param json
     * @return
     */
    @RequestMapping(value = "/login", method = RequestMethod.POST)
    public ResponseEntity<Resp<?>> login(@RequestBody JSONObject json) {
        Assert.hasKeyAndValue(json, "username", "用户名不能为空");

        // 如果密码或者验证码都不在，返回
        if (StringUtils.isBlank(json.getString("password")) && StringUtils.isBlank(json.getString("verifyCode"))) {
            return RespUtil.OK(MsgCode.MSG_ILLEGAL_ARGUMENT);
        }

        UserDtoIn in = BeanConvertUtil.covertBean(json, UserDtoIn.class);

        // 根据 username 查出用户
        String password = in.getPassword();
        in.setPassword("");
        String verifyCode = in.getVerifyCode();
        in.setVerifyCode("");
        in.setPrivilege(null);
        Resp<List<UserDtoOut>> queryOut = userService.listUsersForLogin(in);

        // 查不到用户
        if (queryOut.getTotal() != 1) {
            return RespUtil.OK(MsgCode.MSG_USER_NOT_EXIST);
        }

        UserDtoOut out = queryOut.getData().get(0);
        if (StringUtils.isNotBlank(verifyCode)) {
            // 核对验证码 缓存内存在且与传入的相同
            String cachedCode = cacheService.getKey(properties.getCacheVerifyCodePrefix() + in.getUsername());
            if (StringUtils.isBlank(cachedCode) || !cachedCode.equals(verifyCode)) {
                return RespUtil.OK(MsgCode.MSG_INCORRECT_VERIFY_CODE);
            }
            cacheService.delKey(properties.getCacheVerifyCodePrefix() + in.getUsername());
        } else if (StringUtils.isNotBlank(password)) {
            // 核对密码 密码要相同
            if (!BCrypt.hashpw(password, out.getSalt()).equals(out.getPassword())) {
                return RespUtil.OK(MsgCode.MSG_INCORRECT_PASSWORD);
            }
        } else {
            return RespUtil.OK(MsgCode.MSG_ILLEGAL_ARGUMENT);
        }

        String key = properties.getCacheTokenPrefix() + out.getUid();
        String adminKey = properties.getCacheAdminPrefix() + out.getUid();
        String token = "";
        if (in.isRemember()) {
            token = JWTUtil.sign(
                    out.getUid(), out.getSalt(),
                    new Date(System.currentTimeMillis() + 7L * properties.getJwtExpTime()));
            log.info("exp_time: 七天");
            // 写入token到缓存
            cacheService.setKeyValueExpire(key, token, 7L * properties.getCacheJwtExpTime());
            if (out.getPrivilege()>ServiceConstant.PRIVILEGE_NORMAL){
                cacheService.setKeyValueExpire(adminKey, token, 7L * properties.getCacheJwtExpTime());
            }
        } else {
            token = JWTUtil.sign(
                    out.getUid(), out.getSalt(),
                    new Date(System.currentTimeMillis() + properties.getJwtExpTime()));
            log.info("exp_time: 一天");
            // 写入token到缓存
            cacheService.setKeyValueExpire(key, token, properties.getCacheJwtExpTime());
            if (out.getPrivilege()>ServiceConstant.PRIVILEGE_NORMAL){
                cacheService.setKeyValueExpire(adminKey, token, properties.getCacheJwtExpTime());
            }
        }

        out.setToken(properties.getTokenPrefix() + token);
        out.harmless();
        Resp<UserDtoOut> res = new Resp<>();
        res.setData(out);
        return RespUtil.OK(res);
    }

    /**
     * 刷新用户登录，凭借token刷新
     * @param request
     * @return
     */
    @RequestMapping(value = "/flush", method = RequestMethod.POST)
    public ResponseEntity<Resp<?>> flush(HttpServletRequest request) {
        String authHeader = request.getHeader(JWTUtil.AUTH_HEADER_KEY);
        if (StringUtils.isBlank(authHeader) || !StringUtils.startsWith(authHeader, properties.getTokenPrefix())) {
            return RespUtil.UN_AUTH(MsgCode.MSG_UN_AUTH);
        }
        // 从 token 中解析出 uid
        String token = request.getHeader(JWTUtil.AUTH_HEADER_KEY).replace(properties.getTokenPrefix(), "");
        String uid = JWTUtil.getUserId(token);

        // 查缓存得到用户 token，判断 token 是否一致
        String key = properties.getCacheTokenPrefix() + uid;
        if (!cacheService.hasKey(key) || !cacheService.getKey(key).equals(token)){
            return RespUtil.UN_AUTH(MsgCode.MSG_UN_AUTH);
        }

        UserDtoIn in = new UserDtoIn();
        in.setUid(uid);

        // 查不到用户
        in.setPrivilege(null);
        Resp<List<UserDtoOut>> resp = userService.listUsers(in);
        if (resp.getTotal() != 1) {
            return RespUtil.OK(MsgCode.MSG_USER_NOT_EXIST);
        }

        // 生成 token
        UserDtoOut out = resp.getData().get(0);
        String adminKey = properties.getCacheAdminPrefix() + uid;
        if (!in.isRemember()) {
            token = JWTUtil.sign(
                    out.getUid(), out.getSalt(),
                    new Date(System.currentTimeMillis() + properties.getJwtExpTime()));
            log.info("exp_time: 一天");
            // 写入普通 token 到缓存
            cacheService.setKeyValueExpire(key, token, properties.getCacheJwtExpTime());
            if (out.getPrivilege()>ServiceConstant.PRIVILEGE_NORMAL){
                // 写入 admin 接口 token 到缓存
                cacheService.setKeyValueExpire(adminKey, token, properties.getCacheJwtExpTime());
            }
        } else {
            token = JWTUtil.sign(
                    out.getUid(), out.getSalt(),
                    new Date(System.currentTimeMillis() + 7L * properties.getJwtExpTime()));
            log.info("exp_time: 七天");
            // 写入普通 token 到缓存
            cacheService.setKeyValueExpire(key, token, 7L * properties.getCacheJwtExpTime());
            if (out.getPrivilege()>ServiceConstant.PRIVILEGE_NORMAL){
                // 写入 admin 接口 token 到缓存
                cacheService.setKeyValueExpire(adminKey, token, 7L * properties.getCacheJwtExpTime());
            }
        }
        out.setToken(properties.getTokenPrefix() + token);
        out.harmless();
        Resp<UserDtoOut> res = new Resp<>();
        res.setData(out);
        return RespUtil.OK(res);
    }

    /**
     * 获取验证码
     * @param json
     * @return
     */
    @RequestMapping(value = "/getVerifyCode", method = RequestMethod.POST)
    public ResponseEntity<Resp<?>> getVerifyCode(@RequestBody JSONObject json) {
        Assert.hasKeyAndValue(json, "phone", "用户手机号不能为空");
        String phone = json.getString("phone");

        //写入缓存
        String verifyCode = DataUtil.genVerifyCode();

        String key = properties.getCacheVerifyCodePrefix() + phone;
        cacheService.setKeyValueExpire(key, verifyCode, properties.getVerifyCodeExpTimeMin());

        Resp<String> resp = new Resp<>();
        resp.setData(verifyCode);
        return RespUtil.OK(resp);
    }

    /**
     * 根据手机号验证码重置密码
     * @param json
     * @return
     */
    @RequestMapping(value = "/forget", method = RequestMethod.POST)
    public ResponseEntity<Resp<?>> forget(@RequestBody JSONObject json) {
        Assert.hasKeyAndValue(json, "phone", "用户手机号不能为空");
        Assert.hasKeyAndValue(json, "verifyCode", "验证码不能为空");
        Assert.hasKeyAndValue(json, "password", "密码不能为空");

        UserDtoIn in = BeanConvertUtil.covertBean(json, UserDtoIn.class);

        in.setUsername(in.getPhone());
        String password = in.getPassword();
        in.setPassword("");

        // 核对验证码 成功后删除验证码
        String cachedCode = cacheService.getKey(properties.getCacheVerifyCodePrefix() + in.getUsername());
        if (StringUtils.isBlank(cachedCode) || !cachedCode.equals(in.getVerifyCode())) {
            return RespUtil.OK(MsgCode.MSG_INCORRECT_VERIFY_CODE);
        }
        cacheService.delKey(properties.getCacheVerifyCodePrefix() + in.getUsername());

        // 查找用户
        in.setPrivilege(null);
        Resp<List<UserDtoOut>> queryOut = userService.listUsersForLogin(in);

        // 查不到用户
        if (queryOut.getTotal() != 1) {
            return RespUtil.OK(MsgCode.MSG_USER_NOT_EXIST);
        }
        // 修改密码
        UserDto user = queryOut.getData().get(0);
        in.setUid(user.getUid());
        in.harmless();
        in.setPassword(password);
        Resp<Void> resp = userService.updateUser(in);

        return RespUtil.OK(resp);
    }

    /**
     * 修改用户信息
     * @param json
     * @return
     */
    @RequestMapping(value = "/changeInfo", method = RequestMethod.POST)
    public ResponseEntity<Resp<?>> changeInfo(@RequestBody JSONObject json) {
        Assert.hasKeyAndValue(json, "uid", "uid不能为空");

        UserDtoIn in = BeanConvertUtil.covertBean(json, UserDtoIn.class);
        if(!json.containsKey("privilege")){
            in.setPrivilege(null);
        }
        in.harmless();
        // 根据验证码修改手机号
        if (StringUtils.isNotBlank(json.getString("verifyCode")) && StringUtils.isNotBlank(json.getString("phone"))) {
            // 核对验证码 缓存内存在且与传入的相同
            String cachedCode = cacheService.getKey(properties.getCacheVerifyCodePrefix() + in.getPhone());
            if (StringUtils.isBlank(cachedCode) || !cachedCode.equals(in.getVerifyCode())) {
                return RespUtil.OK(MsgCode.MSG_INCORRECT_VERIFY_CODE);
            }
            cacheService.delKey(properties.getCacheVerifyCodePrefix() + in.getPhone());
            in.setUsername(in.getPhone());
        }

        // 根据新旧密码修改密码
        if (StringUtils.isNotBlank(json.getString("oldPassword")) && StringUtils.isNotBlank(json.getString("newPassword"))) {
            UserDtoIn check = new UserDtoIn();
            check.setUid(in.getUid());
            check.setPrivilege(null);
            Resp<List<UserDtoOut>> listUsers = userService.listUsers(check);
            if (listUsers.getTotal() != 1){
                return RespUtil.OK(MsgCode.MSG_USER_NOT_EXIST);
            }
            UserDtoOut userDto = listUsers.getData().get(0);
            if (!BCrypt.checkpw(json.getString("oldPassword"),userDto.getPassword())){
                return RespUtil.OK(MsgCode.MSG_INCORRECT_PASSWORD);
            }
            in.setPassword(json.getString("newPassword"));
        }

        // 更新用户信息
        Resp<Void> resp = userService.updateUser(in);
        if (resp.getCode().equals(MsgCode.MSG_SUCCESS.getCode())) {
            Resp<List<UserDtoOut>> users = userService.listUsers(in);
            Resp<UserDtoOut> resp1 = new Resp<>();
            UserDtoOut userDtoOut = users.getData().get(0);
            userDtoOut.harmless();
            resp1.setData(userDtoOut);
            return RespUtil.OK(resp1);
        }
        return RespUtil.OK(resp);
    }

    /**
     * 列出某个用户信息
     * @param json
     * @return
     */
    @RequestMapping(value = "/getUser", method = RequestMethod.POST)
    public ResponseEntity<Resp<?>> listUsers(@RequestBody JSONObject json) {
        Assert.hasKeyAndValue(json, "uid", "uid不能为空");

        UserDtoIn in = BeanConvertUtil.covertBean(json, UserDtoIn.class);
        in.setPrivilege(null);
        Resp<List<UserDtoOut>> resp = userService.listUsers(in);
        if (resp.getTotal()>0){
            resp.getData().forEach(UserDto::harmless);
        }
        return RespUtil.OK(resp);
    }
}